package spitfire
{
    import C.unistd.sleep;
    import avmplus.Socket;
    import flash.utils.getTimer;
    
    public class Server
    {
        private var _connections:Array;
        
        private var _socket:Socket;
        private var _server:Client;
        
        //private var _admin:Socket;
        
        private var _address:String;
        private var _port:uint;
        private var _running:Boolean;
        
        private var _refresh:uint = 1000; //ms
        private var _maxConnections:uint = 100; //max clients allowed to connect
        
        private var _allowConnections:Boolean;
        
        public function Server( address:String, port:uint )
        {
            _connections = [];
            
            _address = address;
            _port    = port;
            _running = false;
            
            Socket.prototype.record = function() {};
            
            _socket  = new Socket();
            _socket.reuseAddress = true;
            //_admin   = new Socket();
            
            _allowConnections = false;
        }
        
        public function get allowConnections():Boolean
        {
            return _allowConnections;
        }
        
        public function get maxConnections():uint
        {
            return _maxConnections;
        }
        
        public function get running():Boolean
        {
            return _running;
        }
        
        public function get totalClients():uint
        {
            return _connections.length-1;
        }
        
        public function start():void
        {
            _socket.bind( _port, _address );
            _socket.listen( 128 );
            //_admin.bind( 666, _address );
            //_admin.listen(); //no backlog for admin
            _running = true;
            _allowConnections = true;
            
            _server = new Client( _socket );
            _addClient( _server );
            
            _start();
        }
        
        public function stop():void
        {
            _running = false;
        }
        
        private function _start():void
        {
            onStart();
            
            while( _running )
            {
                _loop();
                sleep( _refresh );
            }
            
            _stop();
        }
        
        public function onStart():void
        {
            trace( "Server started - " + _address + ":" + _port );
        }
        
        public function onStop():void
        {
            trace( "Server stoped" );
        }
        
        private function _stop():void
        {
            _allClient( "drop" );
            
            //_socket.close();
            _server.drop();
            
            onStop();
        }
        
        private function _loop():void
        {
            var i:uint;
            var len:uint = _connections.length;
            var selected:Client;
            var newconn:Socket;
            
            var data:String;
            
            for( i=0; i<len; i++ )
            {
                selected = _connections[i];
                
                if( selected.canRead() )
                {
                    if( selected == _server ) //new connection / read data on server
                    {
                        if( allowConnections )
                        {
                            if( totalClients < maxConnections )
                            {
                                //accept a new client connection
                                newconn = _socket.accept(); //blocking
                                
                                if( !newconn.valid )
                                {
                                    //error
                                    //TODO
                                }
                                else
                                {
                                    //add client
                                    _addClient( new Client( newconn ) );
                                }
                            }
                            else
                            {
                                //max connections reached, can not accept new clients
                            }
                            
                        }
                        else
                        {
                            //connections not allowed
                        }
                        
                    }
                    else //read data from clients
                    {
                        try
                        {
                            data = selected.read();
                        }
                        catch( e:Error )
                        {
                            //error receiving data from client
                            _removeClient( selected );
                        }
                        
                        if( !selected.isValid() )
                        {
                            //not valid client
                            _removeClient( selected );
                        }
                        else
                        {
                            //valid client
                            _interpret( data, selected );
                        }
                    }
                } //canRead
                
                if( selected.isValid() && selected.canWrite() )
                {
                    if( selected == _server )
                    {
                        //write data to server
                        //do nothing
                    }
                    else
                    {
                        //write data to client
                        try
                        {
                            selected.write( "SERVER_" + getTimer() );
                        }
                        catch( e:Error )
                        {
                            //could not write to the client
                        }
                        
                    }
                } //canWrite
                
                
            } //for loop
            
            //admin loop
            //TODO
            
        }
        
        
        
        
        
        
        private function _allClient( call:String ):void
        {
            var i:uint;
            var len:uint = _connections.length;
            var current:Client;
            
            for( i=0; i<len; i++ )
            {
                current = _connections[i];
                
                if( current != _server )
                {
                    current[ call ]();
                }
            }
        }
        
        private function _addClient( client:Client ):void
        {
            _connections.push( client );
        }
        
        private function _removeClient( client:Client ):void
        {
            
        }
        
        private function _broadcast( message:String, exclude:Client = null ):void
        {
            var i:uint;
            var len:uint = _connections.length;
            var current:Client;
            
            for( i=0; i<len; i++ )
            {
                current = _connections[i];
                
                if( current == exclude )
                {
                    continue;
                }
                
                if( current != _server )
                {
                    current.write( message );
                }
            }
        }
        
        
        private function trim( source:String , chars:Array = null ):String
        {
            if( chars == null )
            {
                chars = ["\n","\r","\t"];
            }
            if ( source == null || source == "" )
            {
                return "" ;
            }
            
            var i:int , l:int ;
            
            ////// start
            
            l = source.length ;
            for( i = 0; (i < l) && (chars.indexOf( source.charAt( i ) ) > - 1) ; i++ )
            {
            }
            source = source.substring( i );
            
            ////// end
            
            l = source.length ;
            for( i = source.length - 1; (i >= 0) && (chars.indexOf( source.charAt( i ) ) > - 1) ; i-- )
            {
            }
            source = source.substring( 0, i + 1 ) ;
            
            ////// 
            
            return source ;
        }
        
        private function _interpret( message:String, current:Client = null ):void
        {
            message = trim( message );
            trace( "message = \"" + message + "\"" );
            
            if( message == "killall" )
            {
                stop();
            }
            else
            {
                _broadcast( message, current );
            }
        }
        
    }
}
